package com.example.project3.service.ICS;

import com.example.project3.entity.Tasks;
import com.example.project3.entity.User;
import com.example.project3.mapper.UserMapper;
import com.example.project3.util.CallBack.CallBack;
import com.example.project3.util.Encryption;
import lombok.extern.slf4j.Slf4j;
import net.fortuna.ical4j.data.CalendarBuilder;
import net.fortuna.ical4j.data.CalendarOutputter;
import net.fortuna.ical4j.data.ParserException;
import net.fortuna.ical4j.model.Calendar;
import net.fortuna.ical4j.model.DateTime;
import net.fortuna.ical4j.model.component.VEvent;
import net.fortuna.ical4j.model.property.ProdId;
import net.fortuna.ical4j.model.property.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.time.ZonedDateTime;
import java.util.Map;
import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @ClassName ICSServiceImpl .java
 * @Description empty
 * @Author LiuJinyu
 * @Date 2023/5/27 23:12
 * @Version 1.0
 */
@Service
@Slf4j
public class ICSServiceImpl implements ICSService, CallBack {
    /**
     * 存储用户uid和ics文件名的映射的小缓存
     */
    public static final Map<Integer, String> ICS_NAME_CACHE = new ConcurrentHashMap<>();
    CalendarOutputter outputter;
    private Encryption encryption;
    private UserMapper userMapper;
    private final CalendarBuilder builder;
    private final TimeZone timeZone = TimeZone.getTimeZone("Asia/Shanghai");
    private ProdId prodId;


    public ICSServiceImpl() {
        super();
        createDir();
        builder = new CalendarBuilder();
        outputter = new CalendarOutputter();
    }

    @Override
    public File createICS(User user) {
        File file = randomString(user);
        log.info("email:{} create {} ics file", user.getEmail(), file.getPath());
        try {
            if (file.createNewFile()) {
                user.setIcsFileName(file.getName().substring(file.getName().lastIndexOf('\\') + 1));
            } else {
                throw new IOException();
            }
        } catch (IOException e) {
            e.printStackTrace();
            log.warn("generate ics file failed", e);
        }
        return file;
    }

    @Override
    public byte[] getICSFile(String ICSFileName) {
        StringBuffer buffer = new StringBuffer();
        try (BufferedReader reader = new BufferedReader(new FileReader(ICS_PATH + ICSFileName + ".ics"))) {
            String temp = null;
            while ((temp = reader.readLine()) != null) {
                buffer.append(temp).append("\n");
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return buffer.toString().getBytes(StandardCharsets.UTF_8);
    }


    @Override
    public void callBack() {

    }

    /**
     * use time generate random string
     *
     * @param user
     * @return
     */
    private File randomString(User user) {
        String randomString;
        File file;
        randomString = encryption.str(encryption.MD5(ZonedDateTime.now().toString() + user.getUid()), 8);
        file = new File(ICS_PATH + randomString + ".ics");
        return file;
    }

    private void createDir() {
        File file = new File(ICS_PATH);
        if (!file.exists()) {
            file.mkdirs();
        }
    }

    @Override
    public boolean addEvent(User user, Tasks task) {
        Calendar calendar = null;
        String fileName;
        File file;
        //从cache里面获取ics路径
        if (ICS_NAME_CACHE.containsKey(user.getUid())) {
            fileName = ICS_PATH + ICS_NAME_CACHE.get(user.getUid());
            file = new File(fileName);
        } else {
            //cache中没有数据
            user = userMapper.selectById(user.getUid());
            if (user.getIcsFileName() == null || user.getIcsFileName().length() == 0) {
                //用户数据库记录中没有文件
                file = createICS(user);
                user.setIcsFileName(file.getName());
                userMapper.updateById(user);
                ICS_NAME_CACHE.put(user.getUid(), user.getIcsFileName());
                calendar = getDefaultCalendar();
            } else {
                ICS_NAME_CACHE.put(user.getUid(), user.getIcsFileName());
                fileName = ICS_PATH + ICS_NAME_CACHE.get(user.getUid());
                file = new File(fileName);
            }
        }
        if (calendar == null) {
            try (FileInputStream in = new FileInputStream(file)) {
                calendar = builder.build(in);
            } catch (IOException e) {
                e.printStackTrace();
                log.warn("IO错误", e);
                ICS_NAME_CACHE.remove(user.getUid());
                return false;
            } catch (ParserException e) {
                e.printStackTrace();
                log.warn("{}解析错误", file.getName());
                ICS_NAME_CACHE.remove(user.getUid());
                return false;
            }
        }
        calendar.getComponents().add(parseTask(user, task));
        try (FileOutputStream out = new FileOutputStream(file)) {
            outputter.output(calendar, out);
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
            log.warn("out file IO error");
            return false;
        }
        log.info("{} add an event,task id = {}", file.getName(), task.getTaskId());
        return true;
    }

    private VEvent parseTask(User user, Tasks task) {
        DateTime start = new DateTime(task.getBeginTime().getTime());
        DateTime end = new DateTime(task.getEndTime().getTime());
        VEvent event = new VEvent(start, end, task.getName());
        Uid uid = MyICSUidGenerator.generateUid(user, task);
        event.getProperties().add(uid);
        event.getProperties().add(new TzId("Asia/Shanghai"));
        Description description;
        if (task.getDes() != null) {
            description = new Description(task.getDes());
        } else {
            description = new Description("");
        }
        event.getProperties().add(description);
        return event;
    }

    private Calendar getDefaultCalendar() {
        Calendar calendar = new Calendar();
        calendar.getProperties().add(prodId);
        calendar.getProperties().add(Version.VERSION_2_0);
        calendar.getProperties().add(CalScale.GREGORIAN);
        return calendar;
    }

    @Autowired
    public void setEncryption(Encryption encryption) {
        this.encryption = encryption;
    }


    @Autowired
    public void setUserMapper(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    @Autowired
    public void setGetProdId(ProdId getProdId) {
        this.prodId = getProdId;
    }
}
